// Package container provides dependency injection container
package container

import (
	"context"
	"sync"

	"gitee.com/zhenyangze/gin-framework/internal/pkg/config"
	"gitee.com/zhenyangze/gin-framework/internal/pkg/logger"
	"github.com/asaskevich/EventBus"
	"github.com/go-redis/redis"
	"gorm.io/gorm"
)

// Container holds all application dependencies
type Container struct {
	mu sync.RWMutex

	// Core services
	config  config.Manager
	logger  logger.Logger
	db      *gorm.DB
	redis   *redis.Client
	event   EventBus.Bus

	// Application context
	ctx    context.Context
	cancel context.CancelFunc

	// Lifecycle hooks
	startHooks []func() error
	stopHooks  []func() error
}

// NewContainer creates a new dependency container
func NewContainer() *Container {
	ctx, cancel := context.WithCancel(context.Background())
	
	return &Container{
		ctx:    ctx,
		cancel: cancel,
	}
}

// Config returns the configuration manager
func (c *Container) Config() config.Manager {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.config
}

// Logger returns the logger instance
func (c *Container) Logger() logger.Logger {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.logger
}

// DB returns the database connection
func (c *Container) DB() *gorm.DB {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.db
}

// Redis returns the Redis client
func (c *Container) Redis() *redis.Client {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.redis
}

// Event returns the event bus
func (c *Container) Event() EventBus.Bus {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.event
}

// Context returns the application context
func (c *Container) Context() context.Context {
	return c.ctx
}

// SetConfig sets the configuration manager
func (c *Container) SetConfig(cfg config.Manager) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.config = cfg
}

// SetLogger sets the logger instance
func (c *Container) SetLogger(log logger.Logger) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.logger = log
}

// SetDB sets the database connection
func (c *Container) SetDB(db *gorm.DB) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.db = db
}

// SetRedis sets the Redis client  
func (c *Container) SetRedis(rds *redis.Client) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.redis = rds
}

// SetEvent sets the event bus
func (c *Container) SetEvent(evt EventBus.Bus) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.event = evt
}

// AddStartHook adds a function to be called during startup
func (c *Container) AddStartHook(hook func() error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.startHooks = append(c.startHooks, hook)
}

// AddStopHook adds a function to be called during shutdown
func (c *Container) AddStopHook(hook func() error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.stopHooks = append(c.stopHooks, hook)
}

// Start initializes all services
func (c *Container) Start() error {
	c.mu.RLock()
	hooks := make([]func() error, len(c.startHooks))
	copy(hooks, c.startHooks)
	c.mu.RUnlock()

	for _, hook := range hooks {
		if err := hook(); err != nil {
			return err
		}
	}
	return nil
}

// Stop gracefully shuts down all services
func (c *Container) Stop() error {
	// Cancel context to signal shutdown
	c.cancel()

	c.mu.RLock()
	hooks := make([]func() error, len(c.stopHooks))
	copy(hooks, c.stopHooks)
	c.mu.RUnlock()

	// Execute stop hooks in reverse order
	for i := len(hooks) - 1; i >= 0; i-- {
		if err := hooks[i](); err != nil {
			// Log error but continue shutdown
			if c.logger != nil {
				c.logger.Error("Error during shutdown", "error", err)
			}
		}
	}

	return nil
}

// Health checks the health of all services
func (c *Container) Health() map[string]interface{} {
	health := make(map[string]interface{})

	// Check database
	if c.db != nil {
		sqlDB, err := c.db.DB()
		if err != nil {
			health["database"] = map[string]interface{}{
				"status": "unhealthy",
				"error":  err.Error(),
			}
		} else if err := sqlDB.Ping(); err != nil {
			health["database"] = map[string]interface{}{
				"status": "unhealthy", 
				"error":  err.Error(),
			}
		} else {
			health["database"] = map[string]interface{}{
				"status": "healthy",
			}
		}
	}

	// Check Redis
	if c.redis != nil {
		if err := c.redis.Ping().Err(); err != nil {
			health["redis"] = map[string]interface{}{
				"status": "unhealthy",
				"error":  err.Error(),
			}
		} else {
			health["redis"] = map[string]interface{}{
				"status": "healthy",
			}
		}
	}

	return health
}

// Global container instance (transitional)
var globalContainer *Container
var containerOnce sync.Once

// GetContainer returns the global container instance
// TODO: Remove this after full migration to dependency injection
func GetContainer() *Container {
	containerOnce.Do(func() {
		globalContainer = NewContainer()
	})
	return globalContainer
}

// SetContainer sets the global container instance
// TODO: Remove this after full migration to dependency injection  
func SetContainer(c *Container) {
	globalContainer = c
}